﻿using System;
using System.Globalization;
using System.Runtime.InteropServices;
using System.Text;

namespace SAP.BusinessOne.WebBridge.Framework
{
    class Args
    {
        private IntPtr _data; // data
        private IntPtr _ext; // extended data (external)
        private IntPtr _wos; // current pos of writer
        private IntPtr _ros; // current pos of reader

        public Args(string args)
        {
            _data = (IntPtr)long.Parse(args, NumberStyles.AllowHexSpecifier);
            _ext = IntPtr.Zero;
            _wos = _data;
            _ros = _data + 9; // skip 1 byte for result, 8 bytes for pointer to extended data
        }

        public Args Put(IntPtr value)
        {
            if (IntPtr.Size == 4)
                return Put(value.ToInt32());
            return Put(value.ToInt64());
        }

        public Args Put(byte value) { return Put(new[] { value }); }
        public Args Put(sbyte value) { return Put((byte)value); }

        public Args Put(bool value) { return Put(BitConverter.GetBytes(value)); }
        public Args Put(char value) { return Put(BitConverter.GetBytes(value)); }
        public Args Put(double value) { return Put(BitConverter.GetBytes(value)); }
        public Args Put(float value) { return Put(BitConverter.GetBytes(value)); }
        public Args Put(int value) { return Put(BitConverter.GetBytes(value)); }
        public Args Put(long value) { return Put(BitConverter.GetBytes(value)); }
        public Args Put(short value) { return Put(BitConverter.GetBytes(value)); }
        public Args Put(uint value) { return Put(BitConverter.GetBytes(value)); }
        public Args Put(ulong value) { return Put(BitConverter.GetBytes(value)); }
        public Args Put(ushort value) { return Put(BitConverter.GetBytes(value)); }

        /// <summary>
        /// Puts a string parameter into the extended container.
        /// NOTE: this method can only be called once
        /// </summary>
        public Args PutString(string value)
        {
            // create extended data
            _ext = Marshal.AllocHGlobal(value.Length * 2 + 2);
            Put(_ext); // save pointer of extended data into current data
            _wos = _ext;

            // size
            return Put((ushort)value.Length).

            // data
            Put(Encoding.Unicode.GetBytes(value));
        }

        private Args Put(byte[] data)
        {
            Marshal.Copy(data, 0, _wos, data.Length);
            _wos += data.Length;
            return this;
        }

        public Args Get(out byte value) { value = Get(sizeof(byte))[0]; return this; }
        public Args Get(out sbyte value) { value = (sbyte)Get(sizeof(sbyte))[0]; return this; }

        public Args Get(out bool value) { value = BitConverter.ToBoolean(Get(sizeof(bool)), 0); return this; }
        public Args Get(out char value) { value = BitConverter.ToChar(Get(sizeof(char)), 0); return this; }
        public Args Get(out double value) { value = BitConverter.ToDouble(Get(sizeof(double)), 0); return this; }
        public Args Get(out float value) { value = BitConverter.ToSingle(Get(sizeof(float)), 0); return this; }
        public Args Get(out int value) { value = BitConverter.ToInt32(Get(sizeof(int)), 0); return this; }
        public Args Get(out long value) { value = BitConverter.ToInt64(Get(sizeof(long)), 0); return this; }
        public Args Get(out short value) { value = BitConverter.ToInt16(Get(sizeof(short)), 0); return this; }
        public Args Get(out uint value) { value = BitConverter.ToUInt32(Get(sizeof(uint)), 0); return this; }
        public Args Get(out ulong value) { value = BitConverter.ToUInt64(Get(sizeof(ulong)), 0); return this; }
        public Args Get(out ushort value) { value = BitConverter.ToUInt16(Get(sizeof(ushort)), 0); return this; }

        public Args Get(out string value)
        {
            // read size
            ushort size;
            Get(out size);

            // read data
            var data = Get(size * sizeof(char));
            value = Encoding.Unicode.GetString(data);
            return this;
        }

        private byte[] Get(int size)
        {
            var data = new byte[size];
            Marshal.Copy(_ros, data, 0, data.Length);
            _ros += size;
            return data;
        }
    }
}
